
            <!DOCTYPE html>
            <html lang="en">
            <head>
                <meta charset="UTF-8">
                <title>必知必会，7 张图轻松理解 K8S 集群内服务通信</title>
            </head>
            <body>
            <a href="https://andyoung.blog.csdn.net">原作者博客</a>
            <div id="content_views" class="markdown_views prism-atom-one-light">
                    <svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
                        <path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path>
                    </svg>
                    <p>深入了解支持服务间通信的 3 个原生 k8s 对象：ClusterIP Service、DNS 和 Kube-Proxy。</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/6cf4c16db57f8b0579d2f69506b93b8f.png" alt="图片"></p> 
<h3><a id="_6"></a>传统的服务到服务通信</h3> 
<p>在进入 Kubernetes 生态系统之前，快速了解一下传统的服务到服务通信：通信是通过 IP 地址进行的，因此为了让服务 A 调用服务 B，一种方法是为服务 B 分配一个静态 IP 地址。现在，服务 A 已经知道该 IP 地址（这在处理极少数服务时可能会起作用）或服务 B 使用域名注册自己，并且服务 A 通过 DNS 查找获取服务 B 的联系地址。</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/74071a37a0ee60c88ccedefb3d3f920b.png" alt="传统的服务到服务通信"></p> 
<h3><a id="Kubernetes__14"></a>Kubernetes 网络模型</h3> 
<p>现在在 Kubernetes 集群中，我们拥有构成集群管理组件和一组工作机器（称为节点）的控制平面。这些节点托管 Pod，这些 Pod 将后端微服务作为容器化服务运行。</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/f9109108e9d32f74945c03166f5adf3d.png" alt="集群内的 Pod 到 Pod 通信"></p> 
<p>根据 Kubernetes 网络模型：</p> 
<ol><li>集群中的每个 pod 都有自己唯一的集群范围 IP 地址</li><li>所有 pod 都可以与集群内的每个 pod 通信</li><li>通信在没有 NAT 的情况下发生，这意味着目标 pod 可以看到源 pod 的真实 IP 地址。Kubernetes 认为容器网络或在其上运行的应用程序是可信的，不需要在网络级别进行身份验证。</li></ol> 
<h3><a id="ClusterIP____Pod__28"></a>ClusterIP 服务 ~ 基于 Pod 的抽象</h3> 
<p>既然集群中的每个 pod 都有自己的 IP 地址，那么一个 pod 与另一个 pod 通信应该很容易吧？</p> 
<p>不，因为 Pod 是易变的，每次创建 pod 时都会获得一个新的 IP 地址。所以客户端服务必须以某种方式切换到下一个可用的 pod。</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/a2f16682e58b76b73bcf5714d0242c8d.png" alt="图片"></p> 
<p>Pod 直接相互交谈的问题是另一个目标 Pod 的短暂性（随时可能销毁），其次是发现新 Pod IP 地址。</p> 
<p>因此 Kubernetes 可以在一组 Pod 之上创建一个层，该层可以为该组提供单个 IP 地址并可以提供基本的负载平衡。</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/6823420fbba736ab413920a7f72a6ca5.png" alt="图片"></p> 
<p>通过持久 IP 地址上的 ClusterIP 服务公开的 Pod，客户端与服务对话，而不是直接与 Pod 对话。</p> 
<p>这种抽象是由 Kubernetes 中一个名为<strong>ClusterIP service</strong>的服务对象提供的。它在多个节点上产生，从而在集群中创建单个服务。它可以接收任何端口上的请求并将其转发到 pod 上的任何端口。</p> 
<p>因此，当应用服务 A 需要与服务 B 对话时，它会调用服务 B 对象的 ClusterIP 服务，而不是运行该服务的单个 pod。</p> 
<p>ClusterIP 使用 Kubernetes 中标签和选择器的标准模式来不断扫描匹配选择标准的 pod。Pod 有标签，服务有选择器来查找标签。使用它，可以进行基本的流量拆分，其中新旧版本的微服务在同一个 clusterIP 服务后共存。</p> 
<h3><a id="CoreDNS___52"></a>CoreDNS ~ 集群内的服务发现</h3> 
<p>现在服务 B 已经获得了一个持久的 IP 地址，服务 A 仍然需要知道这个 IP 地址是什么，然后才能与服务 B 通信。</p> 
<p>Kubernetes 支持使用 CoreDNS 进行名称解析。服务 A 应该知道它需要与之通信的 ClusterIP 的名称（和端口）。</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/f26caf24d6256d55c1e7bf79466ffa08.png" alt="图片"></p> 
<ol><li>CoreDNS 扫描集群，每当创建 ClusterIP 服务时，它的条目就会添加到 DNS 服务器（如果已配置，它还会为每个 pod 添加一个条目，但它与服务到服务的通信无关）。</li><li>接下来，CoreDNS 将自己暴露为 cluster IP 服务（默认称为 kube-dns），并且该服务被配置为 pod 中的 nameserver。</li><li>发起请求的 Pod 从 DNS 获取 ClusterIP 服务的 IP 地址，然后可以使用 IP 地址和端口发起请求。</li></ol> 
<h3><a id="Kubeproxy__Service__Pod_DNAT_64"></a>Kube-proxy 打通 Service 和后端 Pod 之间（DNAT）</h3> 
<p>到目前为止，从本文来看，似乎是 ClusterIP 服务将请求调用转发到后端 Pod。但实际上，它是由 Kube-proxy 完成的。</p> 
<p>Kube-proxy 在每个节点上运行，并监视 Service 及其选择的 Pod（实际上是 Endpoint 对象）。</p> 
<ol><li>当节点上运行的 pod 向 ClusterIP 服务发出请求时，kube-proxy 会拦截它。</li><li>通过查看目的 IP 地址和端口，可以识别目的 ClusterIP 服务。并将此请求的目的地替换为实际 Pod 所在的端点地址。</li></ol> 
<h3><a id="_73"></a>如何协同工作？</h3> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/8064d89672e25a73d029aac893a9ab14.png" alt="图片">ClusterIP Service、CoreDNS、客户端 Pod、Kube-Proxy、EndPoint的交互</p> 
<ol><li>目标的 ClusterIP 服务在 CoreDNS 中注册</li><li>DNS 解析：每个 pod 都有一个 resolve.conf 文件，其中包含 CoreDNS 服务的 IP 地址，pod 执行 DNS 查找。</li><li>Pod 使用它从 DNS 收到的 IP 地址和它已经知道的端口来调用 clusterIP 服务。</li><li>目标地址转换：Kube-proxy 将目标 IP 地址更新为服务 B 的 Pod 可用的地址。</li></ol> 
<h3><a id="_82"></a>概括</h3> 
<p>通过本文，我们看到了使服务到服务通信成为可能的原生 Kubernetes 对象，这些细节对应用程序层是隐藏的。</p>
                </div>
            </body>
            </html>
            